#pragma once
#include "match.hpp"
#include "mysql.hpp"
#include "online_user.hpp"
#include "session.hpp"
#include "room.hpp"
#include "log.hpp"
#include "util.hpp"

class GobangServer
{
private:
    std::string _wwwroot;

    websocketServer _wsvr;
    MysqlUserTable _user_tb;
    OnlineUser _online_user;
    SessionManage _session;
    RoomManage _room_manage;
    Match _match;
private:
    void staticHttp(websocketServer::connection_ptr conn)
    {
        websocketpp::http::parser::request req = conn->get_request();
        std::string path = _wwwroot + req.get_uri();
        if(req.get_uri() == "/")
        {
            path += "login.html";
        }
        std::string body = FileUtil::readFile(path);
        if(body == "")
        {   
            std::string notFindPath = _wwwroot + "/404.html";
            body = FileUtil::readFile(notFindPath);
            conn->set_body(body);
            conn->set_status(websocketpp::http::status_code::not_found);
            return;
        }
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
    }
    void httpBack(websocketServer::connection_ptr conn, bool result, const std::string& reason   \
                ,websocketpp::http::status_code::value status)
    {
        Json::Value responseVal;
        responseVal["result"] = result;
        responseVal["reason"] = reason;
        std::string body;
        JsonUtil::Serialization(responseVal, body);
        conn->append_header("Content-Type", "application/json");
        conn->set_body(body);
        conn->set_status(status);
    }
    void reg(websocketServer::connection_ptr conn)
    {
        std::string req = conn->get_request_body();
        Json::Value reqVal;
        //反序列化失败
        if(!JsonUtil::DeSerialization(req, reqVal))
        {
            Log(INFO, "Deserialization failure");
            return httpBack(conn, false, "请求格式错误", websocketpp::http::status_code::bad_request);
        }
        //反序列化成功但解析不出来内容
        if(reqVal["name"].asString().size() == 0 || reqVal["password"].asString().size() == 0) 
        {
            Log(INFO, "Deserialization succeeded but the contents could not be resolved");
            return httpBack(conn, false, "用户名或密码不能为空", websocketpp::http::status_code::bad_request);
        }

        //插入数据库失败，用户名重复
        if(!_user_tb.insert(reqVal))
        {
            Log(INFO, "insert mysql error, Duplicate user name");
            return httpBack(conn, false, "用户名重复", websocketpp::http::status_code::bad_request);
        }
        httpBack(conn, true, "successful", websocketpp::http::status_code::ok);
    }
    void login(websocketServer::connection_ptr conn)
    {
        std::string req = conn->get_request_body();
        Json::Value reqVal;
        //反序列化失败
        if(!JsonUtil::DeSerialization(req, reqVal))
        {
            Log(INFO, "Deserialization failure");
            return httpBack(conn, false, "请求格式错误", websocketpp::http::status_code::bad_request);
        }  
        //反序列化成功但解析不出来内容
        if(reqVal["name"].asString().size() == 0 || reqVal["password"].asString().size() == 0) 
        {
            Log(INFO, "Deserialization succeeded but the contents could not be resolved");
            return httpBack(conn, false, "用户名或密码不能为空", websocketpp::http::status_code::bad_request);
        }
        //账号密码错误
        if(_user_tb.login(reqVal) == false)
        {
            return httpBack(conn, false, "账号或密码错误", websocketpp::http::status_code::bad_request);
        }
        //登陆成功
        uint32_t uid = reqVal["id"].asUInt();
        sessionPtr sPtr = _session.createSession(uid);
        _session.setSessionExpireTime(sPtr->getSid(), SESSIONTIMEOUT);
        std::string sessionStr = "sid=";
        sessionStr += std::to_string(sPtr->getSid());
        conn->append_header("Set-Cookie", sessionStr);
        httpBack(conn, true, "登陆成功", websocketpp::http::status_code::ok);
    }
    uint32_t getCookieSid(const std::string& cookieStr)
    {
        std::vector<std::string> cookies;
        StrUtil::split(cookieStr, "; ", cookies);
        for(auto& str : cookies)
        {
            size_t pos = str.find("=");
            if(pos != std::string::npos)
            {
                if(str.substr(0, pos) == "sid")
                {
                    return atoi(str.substr(pos + 1).c_str());
                }
            }
        }
        return UINT32_MAX;
    }
    void info(websocketServer::connection_ptr conn)
    {
        std::string cookieStr = conn->get_request_header("Cookie");
        //先查看有无Cookie
        if(cookieStr.empty())
        {
            return httpBack(conn, false, "没有Cookie信息,请先登陆", websocketpp::http::status_code::bad_request);
        }
        //查看有无sid
        uint32_t sid = getCookieSid(cookieStr);
        Log(INFO, "get info from sid : %d", sid);
        if(sid == UINT32_MAX)
        {
            return httpBack(conn, false, "Cookie信息不包含sid", websocketpp::http::status_code::bad_request);
        }
        sessionPtr sPtr = _session.findSessionFromSid(sid);
        if(sPtr == nullptr)
        {
            return httpBack(conn, false, "登陆过期请重新登陆", websocketpp::http::status_code::bad_request);
        }
        Json::Value info;
        if(_user_tb.getById(sPtr->getUid(), info) == false)
        {
            return httpBack(conn, false, "没有该用户信息,请重新登陆", websocketpp::http::status_code::bad_request);
        }

        std::string body;
        JsonUtil::Serialization(info, body);
        conn->append_header("Content-Type", "application/json");
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
        _session.setSessionExpireTime(sPtr->getSid(), SESSIONTIMEOUT);
    }
    void httpHandler(websocketpp::connection_hdl hdl)
    {
        websocketServer::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string method = req.get_method();
        std::string uri = req.get_uri();
        if(method == "POST" && uri == "/reg")
        {
            reg(conn);
        }
        else if(method == "POST" && uri == "/login")
        {
            login(conn);
        }
        else if(method == "GET" && uri == "/info")
        {
            info(conn);
        }
        else
        {
            staticHttp(conn);
        }
    }

    void jsonSend(websocketServer::connection_ptr conn, Json::Value val)
    {
        std::string outstr;
        JsonUtil::Serialization(val, outstr);
        conn->send(outstr);
    } 

    sessionPtr getSptrfromConn(websocketServer::connection_ptr conn, std::string optype)
    {
        std::string cookieStr = conn->get_request_header("Cookie");
        //先查看有无Cookie
        if(cookieStr.empty())
        {
            Json::Value responseJson;
            responseJson["optype"] = optype;
            responseJson["result"] = false;
            responseJson["reason"] = "没有Cookie信息,请先登陆";
            jsonSend(conn, responseJson);
            return nullptr;
        }
        //查看有无sid
        uint32_t sid = getCookieSid(cookieStr);
        Log(INFO, "get info from sid : %d", sid);
        if(sid == UINT32_MAX)
        {
            Json::Value responseJson;
            responseJson["optype"] = optype;
            responseJson["result"] = false;
            responseJson["reason"] = "Cookie信息不包含sid";
            jsonSend(conn, responseJson);
            return nullptr;
        }
        sessionPtr sPtr = _session.findSessionFromSid(sid);
        if(sPtr == nullptr)
        {
            Json::Value responseJson;
            responseJson["optype"] = optype;
            responseJson["result"] = false;
            responseJson["reason"] = "登陆过期请重新登陆";
            jsonSend(conn, responseJson);
            return nullptr;
        }
        return sPtr;
    }

    void createHall(websocketServer::connection_ptr conn)
    {
        //获取sPtr
        sessionPtr sPtr = getSptrfromConn(conn, "hall_ready");
        if(sPtr == nullptr)
        {
            return;
        }
        //查看有无重复登陆
        if(_online_user.findInHall(sPtr->getUid()) || _online_user.findInRoom(sPtr->getUid()))
        {
            Json::Value responseJson;
            responseJson["optype"] = "hall_ready";
            responseJson["result"] = false;
            responseJson["reason"] = "重复登陆请重试";
            jsonSend(conn, responseJson);
            return;
        }

        //加入大厅链接
        _online_user.addUserHall(sPtr->getUid(), conn);
        //session改为永久存在
        _session.setSessionExpireTime(sPtr->getSid(), -1);
        //返回信息
        Json::Value responseJson;
        responseJson["optype"] = "hall_ready";
        responseJson["result"] = true;
        jsonSend(conn, responseJson);
    }

    void createRoom(websocketServer::connection_ptr conn)
    {
        //获取sPtr
        sessionPtr sPtr = getSptrfromConn(conn, "room_ready");
        if(sPtr == nullptr)
        {
            return;
        }
        //查看有无重复登陆
        if(_online_user.findInHall(sPtr->getUid()) || _online_user.findInRoom(sPtr->getUid()))
        {
            Json::Value responseJson;
            responseJson["optype"] = "room_ready";
            responseJson["result"] = false;
            responseJson["reason"] = "重复登陆请重试";
            jsonSend(conn, responseJson);
            return;
        }
        //检测用户是否在房间
        roomPtr rmPtr = _room_manage.findRoomFromUid(sPtr->getUid());
        if(rmPtr == nullptr)
        {
            Json::Value responseJson;
            responseJson["optype"] = "room_ready";
            responseJson["result"] = false;
            responseJson["reason"] = "用户不在房间,请先匹配";
            jsonSend(conn, responseJson);
            return;
        }

        //加入房间链接
        _online_user.addUserRoom(sPtr->getUid(), conn);
        //session改为永久存在
        _session.setSessionExpireTime(sPtr->getSid(), -1);
        //返回信息
        Json::Value responseJson;
        responseJson["optype"] = "room_ready";
        responseJson["room_id"] = rmPtr->getRoomid();
        responseJson["self_id"] = sPtr->getUid();
        responseJson["white_id"] = rmPtr->getBlacker();
        responseJson["black_id"] = rmPtr->getWhiter();
        responseJson["result"] = true;
        jsonSend(conn, responseJson);
    }
    void OpenHandler(websocketpp::connection_hdl hdl)
    {
        websocketServer::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        Log(INFO, "the uri is : %s", uri.c_str());
        if(uri == "/hall")
        {
            //建立游戏大厅websocket
            createHall(conn);
        }
        else if(uri == "/room")
        {
            //建立房间大厅websocket
            createRoom(conn);
        }
    }
    void closeHall(websocketServer::connection_ptr conn)
    {
        sessionPtr sPtr = getSptrfromConn(conn, "hall_ready");
        if(sPtr == nullptr)
        {
            return;
        }

        _online_user.deleteUserHall(sPtr->getUid());
        _session.setSessionExpireTime(sPtr->getSid(), SESSIONTIMEOUT);
    }
    void closeRoom(websocketServer::connection_ptr conn)
    {
        sessionPtr sPtr = getSptrfromConn(conn, "room_ready");
        if(sPtr == nullptr)
        {
            return;
        }

        _online_user.deleteUserRoom(sPtr->getUid());
        _session.setSessionExpireTime(sPtr->getSid(), SESSIONTIMEOUT);
        //将用户从房间移除
        _room_manage.removeRoomUser(sPtr->getUid());
    }
    void CloseHandler(websocketpp::connection_hdl hdl)
    {
        websocketServer::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            //关闭游戏大厅websocket
            closeHall(conn);
        }
        else if(uri == "/room")
        {
            //关闭房间大厅websocket
            closeRoom(conn);
        }
    }
    void gameHallMessage(websocketServer::connection_ptr conn, websocketServer::message_ptr msg)
    {
        Json::Value requstJson;
        JsonUtil::DeSerialization(msg->get_payload(), requstJson);
        if(!requstJson["optype"].isNull() && requstJson["optype"].asString() == "match_start")
        {
            //获取sPtr
            sessionPtr sPtr = getSptrfromConn(conn, "match_start");
            if(sPtr == nullptr)
            {
                return;
            }

            _match.addPlayer(sPtr->getUid());
            Json::Value responseJson;
            responseJson["optype"] = "match_start";
            responseJson["result"] = true;
            jsonSend(conn, responseJson);
        }
        else if(!requstJson["optype"].isNull() && requstJson["optype"].asString() == "match_stop")
        {
            //获取sPtr
            sessionPtr sPtr = getSptrfromConn(conn, "match_stop");
            if(sPtr == nullptr)
            {
                return;
            }

            _match.removePlayer(sPtr->getUid());
            Json::Value responseJson;
            responseJson["optype"] = "match_stop";
            responseJson["result"] = true;
            jsonSend(conn, responseJson);
        }
        else
        {
            Json::Value responseJson;
            responseJson["optype"] = "match_start";
            responseJson["result"] = false;
            responseJson["reason"] = "请求格式错误";
            jsonSend(conn, responseJson);
        }
    }
    void gameRoomMessage(websocketServer::connection_ptr conn, websocketServer::message_ptr msg)
    {
        //获取sPtr
        sessionPtr sPtr = getSptrfromConn(conn, "put_chess");
        if(sPtr == nullptr)
        {
            return;
        }
        //检测用户是否在房间
        roomPtr rmPtr = _room_manage.findRoomFromUid(sPtr->getUid());
        if(rmPtr == nullptr)
        {
            Json::Value responseJson;
            responseJson["optype"] = "put_chess";
            responseJson["result"] = false;
            responseJson["reason"] = "用户不在房间,请先匹配";
            jsonSend(conn, responseJson);
            return;
        }
        Json::Value requstJson;
        if(JsonUtil::DeSerialization(msg->get_payload(), requstJson) == false)
        {
             Json::Value responseJson;
            responseJson["optype"] = "put_chess";
            responseJson["result"] = false;
            responseJson["reason"] = "无法解析字符串";
            jsonSend(conn, responseJson);
            return;
        }

        rmPtr->handlerRequest(requstJson);
    }
    void messageHandler(websocketpp::connection_hdl hdl, websocketServer::message_ptr msg)
    {
        websocketServer::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            //关闭游戏大厅websocket
            gameHallMessage(conn, msg);
        }
        else if(uri == "/room")
        {
            //关闭房间大厅websocket
            gameRoomMessage(conn, msg);
        }
    }
public:
    GobangServer(const std::string wwwroot)
        : _wwwroot(wwwroot), _session(&_wsvr)
        , _room_manage(&_user_tb, &_online_user)
        , _match(&_user_tb, &_online_user, &_room_manage)
    {
        _wsvr.set_access_channels(websocketpp::log::alevel::none);
        // 初始化asio
        _wsvr.init_asio();
        _wsvr.set_reuse_addr(true);
        
        //注册回调函数
        _wsvr.set_http_handler(std::bind(&GobangServer::httpHandler, this, std::placeholders::_1));
        _wsvr.set_open_handler(std::bind(&GobangServer::OpenHandler, this, std::placeholders::_1));
        _wsvr.set_close_handler(std::bind(&GobangServer::CloseHandler, this, std::placeholders::_1));
        _wsvr.set_message_handler(std::bind(&GobangServer::messageHandler, this, std::placeholders::_1, std::placeholders::_2));
    }
    void run(uint16_t port)
    {
        //bind端口
        _wsvr.listen(8080);
        //接收链接
        _wsvr.start_accept();
        //启动服务
        _wsvr.run();
    }
};